package com.appspot.web2stanza;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;

import net.htmlparser.jericho.*;
import com.appspot.web2stanza.velocity.Builder;
import com.appspot.web2stanza.velocity.Epub;
import com.appspot.web2stanza.velocity.Item;

public class HTMLParser {

  private static final String DEFAULT_LANG = "en";

  private Source src;
  private String lang;

  public HTMLParser(Reader reader, String lang) throws IOException {
    src = new Source(reader);
    src.fullSequentialParse();

    if (lang != null) {
      this.lang = lang;
    } else {
      Element elem = src.getFirstElement(HTMLElementName.HTML);
      Attributes attrs = elem.getAttributes();
      String htmlLang = attrs.getValue("lang");
      if (htmlLang != null) {
        this.lang = htmlLang;
      } else {
        String metaLang = null;
        List<StartTag> metaTags = src.getAllStartTags(HTMLElementName.META);
        for (StartTag tag : metaTags) {
          Attributes metaAttrs = tag.getAttributes();
          if ("Content-Language".equalsIgnoreCase(metaAttrs.getValue("http-equiv"))) {
            metaLang = metaAttrs.getValue("content");
            break;
          }
        }
        if (metaLang != null) {
          this.lang = metaLang;
        } else {
          this.lang = DEFAULT_LANG;
        }
      }
    }
  }

  public void setInfo(Epub epub) {
    setTitle(epub);
    setLanguage(epub);
    setAuthor(epub);
    setPublisher(epub);
    setDescription(epub);
    //setExternals(epub);
  }

  public void setTitle(Epub epub) {
    Element elem = src.getFirstElement(HTMLElementName.TITLE);
    if (elem != null) {
      epub.setTitle(elem.getContent().toString());
    }
  }

  public void setLanguage(Epub epub) {
    epub.setLanguage(lang);
  }

  public void setAuthor(Epub epub) {
    String author = null;

    // set with meta tag
    List<StartTag> metaTags = src.getAllStartTags(HTMLElementName.META);
    for (StartTag tag : metaTags) {
      Attributes attrs = tag.getAttributes();
      String metaName = attrs.getValue("name");
      if ("author".equalsIgnoreCase(metaName) ||
          "dc.creator".equalsIgnoreCase(metaName) ||
          "dc:creator".equalsIgnoreCase(metaName)) {
        author = attrs.getValue("content");
        break;
      }
    }

    // set with link tag
    if (author == null) {
      List<StartTag> linkTags = src.getAllStartTags(HTMLElementName.LINK);
      for (StartTag tag : linkTags) {
        Attributes attrs = tag.getAttributes();
        String linkRel = attrs.getValue("rel");
        if ("index".equalsIgnoreCase(linkRel)) {
          author = attrs.getValue("title");
          break;
        }
      }
    }

    if (author != null) {
      epub.setAuthor(author);
    }
  }

  public void setPublisher(Epub epub) {
  }

  public void setDescription(Epub epub) {
    String description = null;

    // set with meta tag
    List<StartTag> metaTags = src.getAllStartTags(HTMLElementName.META);
    for (StartTag tag : metaTags) {
      Attributes attrs = tag.getAttributes();
      String metaName = attrs.getValue("name");
      if ("description".equalsIgnoreCase(metaName)) {
        description = attrs.getValue("content");
        break;
      }
    }

    epub.setDescription(description);
  }

  public void html2Xhtml(OutputStream os) throws IOException {
    OutputDocument output = new OutputDocument(src);

    // set XML declaration
    String xmlDecl = "<?xml version=\"1.0\" encoding=\"" + Builder.ENCODING + "\"?>\n";
    StartTag xmlDeclTag = src.getNextStartTag(0, StartTagType.XML_DECLARATION);
    if (xmlDeclTag != null) {
      output.replace(xmlDeclTag, xmlDecl.trim()); 
    } else {
      output.insert(0, xmlDecl);
    }

    // set DOCTYPE tag
    String xhtmlDoctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n";
    StartTag doctypeTag = src.getNextStartTag(0, StartTagType.DOCTYPE_DECLARATION);
    if (doctypeTag != null) {
      output.replace(doctypeTag, xhtmlDoctype); 
    } else {
      output.insert(0, xhtmlDoctype);
    }

    // set XMLNS
    String xhtmlTag = "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"" + lang + "\" lang=\"" + lang + "\">";
    StartTag htmlTag = src.getNextStartTag(0, HTMLElementName.HTML);
    output.replace(htmlTag, xhtmlTag);

    // set charset
    String charset = "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\" />";
    Pattern charsetRegex = Pattern.compile("Content-Type", Pattern.CASE_INSENSITIVE);
    Element charsetElem = src.getNextElement(0, "http-equiv", charsetRegex);
    if (charsetElem != null) {
      output.replace(charsetElem, charset);
    } else {
      output.insert(0, charset);
    }

    // remove script, iframe, stylesheet
    List<Element> removeElems = new ArrayList<Element>();
    removeElems.addAll(src.getAllElements("script"));
    removeElems.addAll(src.getAllElements("iframe"));
    removeElems.addAll(src.getAllElements("style"));
    List<StartTag> linkTags = src.getAllStartTags(HTMLElementName.LINK);
    for (StartTag tag : linkTags) {
      Attributes attrs = tag.getAttributes();
      String linkRel = attrs.getValue("rel");
      if ("stylesheet".equalsIgnoreCase(linkRel)) {
        removeElems.add(tag.getElement());
      }
    }

    output.remove(removeElems);

    // tidy output
    Source xmlSrc = new Source(CharStreamSourceUtil.getReader(output));
    SourceFormatter sf = new SourceFormatter(xmlSrc);
    sf.setTidyTags(true).setIndentString("  ").writeTo(new OutputStreamWriter(os, Builder.ENCODING));
  }

  /**
   * Return list of attachement(css, img) path
   */
  public void setExternals(Epub epub) {
    List<Item> list = epub.getItemList();

    // get with link tag
    List<StartTag> linkTags = src.getAllStartTags(HTMLElementName.LINK);
    int seq = 1;
    for (StartTag tag : linkTags) {
      Attributes attrs = tag.getAttributes();
      String linkRel = attrs.getValue("rel");
      if ("stylesheet".equalsIgnoreCase(linkRel)) {
        String href = attrs.getValue("href");
        String type = attrs.getValue("type");
        if (href != null &&
            ! href.contains(":") &&
            type != null) {
          Item item = new Item();
          item.setId(linkRel + "." + seq);
          item.setHref(href);
          item.setMediaType(type);
          item.setIsExternal(true);
          item.setIsToc(false);
          list.add(item);
          seq++;
        }
      }
    }
  }

}
